import base64
import random

import allure
import jsonpath
import pymysql
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from deepdiff import DeepDiff
from pymysql import cursors

from core.globalContext import Global_Context


class KeyWords:

    @allure.step("参数数据：发送Get请求")
    def request_get(self, **kwargs):
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
        }

        response = requests.get(**request_data)
        Global_Context().set_dic("current_response", response)  # 默认设置成全局变量
        return response

    # @allure.step("参数数据：发送Get请求")
    # def request_get(self, **kwargs):
    #     response = requests.get(**kwargs)
    #     # 把对应的当前的响应数据进行存储一下
    #     Global_Context().set_dic("current_response", response)
    #     return response

    @allure.step("参数数据：发送Post请求")
    def request_post(self, **kwargs):
        response = requests.post(**kwargs)
        Global_Context().set_dic("current_response", response)
        return response

    @allure.step("参数数据：发送Post-form_urlencoded请求")
    def request_post_form_urlencoded(self, **kwargs):
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        data = kwargs.get("DATA", None)
        headers = kwargs.get("HEADERS", None)

        request_data = {
            "url": url,
            "params": params,
            "data": data,
            "headers": headers
        }

        response = requests.post(**request_data)
        Global_Context().set_dic("current_response", response)
        # print(response.json())
        return response

    @allure.step("参数数据：提取JSON数据并存储到全局变量")
    def ex_jsonData(self, **kwargs):
        """
        提取Json数据
        1. 提取的数据是什么
        2. 提取表达式是什么
        3. 提取的下标
        4. 保存到全局的变量名是什么
        :param EXVALUE: 提取json的表达式
        :param INDEX: 下标，默认为0
        :param VARNAME: 存储到全局变量的变量名
        :return:
        """
        response = Global_Context().get_dic("current_response").json()
        EXVALUE = kwargs.get("EXVALUE", None)
        INDEX = kwargs.get("INDEX", 0)
        if INDEX is None:
            INDEX = 0
        ex_data = jsonpath.jsonpath(response, EXVALUE)[INDEX]
        print("当前提取的数据为:", ex_data)
        # TODO 4: 通过提取的数据存储成对应变量到全局变量当中
        Global_Context().set_dic(kwargs["VARNAME"], ex_data)
        print("-----------------------")
        print(Global_Context().show_dict())
        print("-----------------------")
        return ex_data

    # 封装一个断言的方法即可
    @allure.step("断言处理：通过响应数据进行断言")
    def assert_text_comparators(self, **kwargs):
        """
        :param kwargs:
        :param expected: 期望结果
        :param value: 实际结果
        :param op_str: 比较运算符 ：> = < ...
        :return:
        """

        # 字典 + 匿名函数 : lambda 需要传的参数：执行的操作
        comparators = {
            # key: value
            # 操作符是A: 执行A
            ">": lambda a, b: a > b,
            "<": lambda a, b: a < b,
            "==": lambda a, b: a == b,
            ">=": lambda a, b: a >= b,
            "<=": lambda a, b: a <= b,
            "!=": lambda a, b: a != b
        }

        if kwargs["OP_STR"] not in comparators:
            raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")

        if not comparators[kwargs["OP_STR"]](kwargs['VALUE'], kwargs["EXPECTED"]):
            # 返回的是一个Flase ，才会进入到这个位置：
            raise AssertionError(f"{kwargs['VALUE']} {kwargs['OP_STR']} {kwargs['EXPECTED']} 失败")

    # @allure.step("参数数据：提取MySQL数据库数据并存储到全局变量")
    # def ex_mysqlData(self, **kwargs):
    #     """
    #     查询对应的数据并且存储到对应的全局变量当中。
    #     :param mysql_name: 你要使用的数据库的名字
    #     :param sql: 你要执行的SQL
    #     :param var_name: 变量名 : 多个逗号分割
    #     :return:
    #     """
    #     config = {"cursorclass": cursors.DictCursor}
    #     db_config = Global_Context().get_dic("_database")[kwargs["mysql_name"]]
    #     # 数据库的连接信息
    #     config.update(db_config)
    #     connection = pymysql.connect(**config)
    #     cursor = connection.cursor()
    #     cursor.execute(kwargs["sql"])
    #     sql_data = cursor.fetchall()
    #     cursor.close()
    #
    #     var_names = kwargs["var_name"].split(",")
    #
    #     result = {}
    #     for i, data in enumerate(sql_data, start=1):
    #
    #         for j, value in enumerate(var_names):
    #             result[f'{var_names[j]}_{i}'] = data.get(var_names[j])  # 根据变量名称找读取出来的内容
    #
    #     # 写入到全局变量
    #     Global_Context().set_by_dict(result)
    #     # print(Global_Context().show_dict())

    # TODO :扩展： 对数据进行加密处理
    @allure.step("加密处理：把数据进行AES加密：")
    def encrypt_aes(self, **kwargs):
        """
        对数据进行AES加密，写到全局变量当中
        :param data: 需要加密的初始数据
        :param VARNAME: 加密之后数据存储到哪个变量当中
        :return:
        """
        key = b'1234567812345678'
        # 创建AES对象，ECB模式
        cipher = AES.new(key, AES.MODE_ECB)
        # 加密前需要PKCS7填充
        data = kwargs["data"].encode("utf-8")
        padded_data = pad(data, AES.block_size)
        encrypted = cipher.encrypt(padded_data)
        # 加密后转为Base64编码
        encrypt_data = base64.b64encode(encrypted).decode("utf-8")

        # TODO 4: 通过提取的数据存储成对应变量到全局变量当中
        Global_Context().set_dic(kwargs["VARNAME"], encrypt_data)

        # print("-----------------------")
        # print(Global_Context().show_dict())
        # print("-----------------------")

    @allure.step("断言处理：通过deepdiff进行全量断言")
    def assert_json_DeepDiff(self, **kwargs):
        """
        对比2个对应的数据的差异
        :param json1:
        :param json2:
        :param 过滤字段: exclude_paths
        :param 忽略顺序: ignore_order
        :param 忽略大小写:ignore_string_case
        :return:
        """
        json1 = kwargs["json1"]
        json2 = kwargs["json2"]

        # 对比条件：有可能没有传递 ：get
        exclude_paths = kwargs.get("过滤字段", None)
        ignore_order = kwargs.get("忽略顺序", None)
        ignore_string_case = kwargs.get("忽略大小写", False)

        # 组装起来，变成key：value方式传递
        screen_data = {
            "exclude_paths": exclude_paths,
            "ignore_order": ignore_order,
            "ignore_string_case": ignore_string_case
        }

        diff_dada = DeepDiff(json1, json2, **screen_data)

        # 断言进行处理
        assert not diff_dada, f"全量断言失败：{diff_dada}"

    @allure.step("参数数据：根据变量名设置成变量")
    def generate_name(self, **kwargs):
        data = "hami" + str(random.randint(0, 9999))
        Global_Context().set_dic(kwargs["VARNAME"], data)  # 根据变量名设置成变量

    @allure.step("参数数据：发送Post请求-row_json")
    def request_post_row_json(self, **kwargs):
        """
        发送Post请求
        """
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)
        data = kwargs.get("DATA", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "json": data,
        }

        response = requests.post(**request_data)
        Global_Context().set_dic("current_response", response)  # 默认设置成全局变量
        return response

    @allure.step("参数数据：发送Post请求-form_data")
    def request_post_form_data(self, **kwargs):
        """
        发送Post请求
        """
        url = kwargs.get("URL", None)
        files = kwargs.get("FILES", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)
        data = kwargs.get("DATA", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "data": data,
            "files": eval(files)  # 变成字典格式
        }

        response = requests.post(**request_data)
        print("response", response.json())
        Global_Context().set_dic("current_response", response)  # 默认设置成全局变量
        return response

    @allure.step("参数数据：发送Delete请求")
    def request_delete(self, **kwargs):
        """
        发送Post请求
        """
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)
        data = kwargs.get("DATA", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "json": data,
        }

        response = requests.delete(**request_data)
        Global_Context().set_dic("current_response", response)  # 默认设置成全局变量
        return response

    # TODO: 扩展 - 数据库提取的方法
    @allure.step("参数数据：提取数据库数据并存储")
    def ex_mysqlData(self, **kwargs):
        """
        数据库 : 数据库的名称
        引用变量：数据库要存储的变量名，列表格式
        存储到全局变量：{“变量名_下标”:数据}
        """
        config = {"cursorclass": cursors.DictCursor}
        # 读取全局变量 - 根据选择的数据 读取指定的数据库配置 连接对应的数据库
        db_config = Global_Context().get_dic("_database")[kwargs["数据库"]]
        config.update(db_config)

        con = pymysql.connect(**config)
        cur = con.cursor()
        cur.execute(kwargs["SQL"])
        rs = cur.fetchall()
        cur.close()
        con.close()
        print("数据库查询结果:", rs)

        var_names = kwargs["引用变量"].split(",")
        result = {}
        for i, data in enumerate(rs, start=1):
            for j, value in enumerate(var_names):
                result[f'{var_names[j]}_{i}'] = data.get(var_names[j])  # 根据变量名称找读取出来的内容
        Global_Context().set_by_dict(result)
